﻿namespace clowncar.Utils
{
    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Reflection;

    static class AssemblyUtils
    {
        static Dictionary<string, Assembly> libs = new Dictionary<string, Assembly>();
        static string[] libNames;

        public static Assembly AssemblyResolver(object sender, ResolveEventArgs args)
        {
            string shortName = new AssemblyName(args.Name).Name;

            // ignore assembly resolve on Serializers, this is an artefact of .net serialization's implementation 
            if (shortName.EndsWith("Serializers"))
            {
                return null;
            }

            // already know about this one?
            if (libs.ContainsKey(shortName))
            {
                return libs[shortName];
            }

            string relevantName = AssemblyUtils.GetResourceName(shortName);
            if (relevantName == null)
            {
                return null;
            }

            libs[shortName] = LoadAssembly(relevantName);

            return libs[shortName];
        }

        private static Assembly LoadAssembly(string relevantName)
        {
            using (Stream s = Assembly.GetExecutingAssembly().
                   GetManifestResourceStream(relevantName))
            {
                byte[] data = new BinaryReader(s).ReadBytes((int)s.Length);
                Assembly a = Assembly.Load(data);
                return a;
            }
        }

        private static string GetResourceName(string shortName)
        {
            if (libNames == null)
            {
                libNames = Assembly.GetExecutingAssembly().GetManifestResourceNames();
            }

            string relevantName = null;

            foreach (string name in libNames)
            {
                if (name.Contains("." + shortName + ".dll") || name.EndsWith("." + shortName))
                {
                    relevantName = name;
                }
            }

            return relevantName;
        }

 
        internal static string ExtractResource(string filename, string directory)
        {
            Assembly a = Assembly.GetExecutingAssembly();
            string filePath = null;
            string path = null;

            string shortName = new AssemblyName(filename).Name;
            string relevantName = AssemblyUtils.GetResourceName(shortName);
            if (relevantName == null) {
                throw new ArgumentException("No such resource -- " + filename);
            } 

            using (Stream stream = a.GetManifestResourceStream(relevantName))
            {
                filePath = Path.Combine(directory, filename);
                path = filePath;
                var fileInfoOutputFile = new FileInfo(filePath);

                using (FileStream streamToOutputFile = fileInfoOutputFile.OpenWrite())
                {
                    const int size = 4096;

                    byte[] bytes = new byte[size];
                    int numBytes;
                    while ((numBytes = stream.Read(bytes, 0, size)) > 0)
                    {
                        streamToOutputFile.Write(bytes, 0, numBytes);
                    }
                }
            }

            return filePath;
        }
    }
}